using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

using Dashboard;
using Dashboard.Util;
using Dashboard.Engine;

namespace Dashboard.Engine.Transports
{
	// This transport reads files from the messages 
	// directory as XML fragments, and injects them 
	// into the engine. This is usable when sending 
	// clues from shell scripts or other situations when 
	// you don't want to spawn a new process or can't 
	// start a socket connection.
	public class FileTransport : IComponent
	{
		private FileSystemWatcher watcher;

		private string messages_directory = Path.Combine (PathFinder.UserData, "Messages");
		private const string messages_pattern = "*.xml";
		
		public void Initialize ()
		{
			// Make sure that the directory we monitor exists
			// and that it's empty to start with, we're not 
			// interested in old messages (at least not old
			// clues).

			if (!Directory.Exists (messages_directory))
				Directory.CreateDirectory (messages_directory);

			foreach (string file in Directory.GetFiles (messages_directory, messages_pattern))
				File.Delete (file);

			// Monitor our message queue directory. Use 
			// inotify if we're on Linux and whatever
			// available otherwise via the FSW.
#if LINUX && PIGS_CAN_FLY
			if (Inotify.Enabled) {
				Inotify.Subscribe (messages_directory,
						   OnInotifyEvent, 
						   Inotify.EventType.Create | Inotify.EventType.CloseWrite);
			} else {
#endif
	                        watcher = new FileSystemWatcher ();
				watcher.Path = messages_directory;
				watcher.Filter = messages_pattern;
				watcher.IncludeSubdirectories = false;
				watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
				
				watcher.Created += OnFileSystemEvent;
				watcher.EnableRaisingEvents = true;
#if LINUX && PIGS_CAN_FLY
			}
#endif
		}

		public void Dispose ()
		{
			if (watcher != null)
				watcher.EnableRaisingEvents = false;
		}

		private void InjectMessageFile (string message_file)
		{
			if (!File.Exists (message_file))
				return;

			Engine.Log.Debug ("Injecting message ({0})", message_file);

			Message message = null;

			// Deserialize the message and inject it into
			// the engine.

			try {
				message = Message.Serializer.Deserialize (File.OpenRead (message_file)) as Message;
				
				if (message != null)
					Engine.MessageBroker.Send (message);
			} catch (Exception ex) {
				Engine.Log.Error ("Exception caught while deserializing message file ({0}: {1})", 
						  message_file,
						  ex.Message);
			} finally {
				File.Delete (message_file);
			}
		}

		private void OnFileSystemEvent (object o, FileSystemEventArgs args)
		{
			InjectMessageFile (args.FullPath);
		}

		private void OnInotifyEvent (Inotify.Watch watch, string path, string subitem, string srcpath, Inotify.EventType type)
		{
			if (!StringFu.GlobMatch (messages_pattern, subitem))
				return;

			InjectMessageFile (Path.Combine (path, subitem));
		}

		public string Name
		{
			get { return "FileTransport"; }
		}
		
		public string Description
		{
			get { return "Reads messages from the disk and injects them into the engine"; }
		}
	}
}
